package com.hzw.saas.service.storage.service;

import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.Resource;

import com.hzw.saas.api.storage.IResourceFileService;
import com.hzw.saas.api.storage.IResourceFolderService;
import com.hzw.saas.api.storage.IResourceService;
import com.hzw.saas.api.storage.bo.ResourceBO;
import com.hzw.saas.api.storage.bo.ResourceFileBO;
import com.hzw.saas.api.storage.bo.ResourceFolderStructBO;
import com.hzw.saas.api.storage.bo.ResourcesBO;
import com.hzw.saas.api.user.IUserAccountService;
import com.hzw.saas.api.user.bo.UserAccountBO;
import com.hzw.saas.common.config.query.LogicQuery;
import com.hzw.saas.common.util.enums.ConflictModeEnum;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author zzl
 * @since 07/02/2021
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ResourceServiceImpl implements IResourceService {

    private final IUserAccountService userAccountService;

    @Resource
    private IResourceFolderService folderService;
    @Resource
    private IResourceFileService fileService;


    @Override
    public void assembleBaseInfo(ResourceBO resourceBO) {
        this.assembleBaseInfo(ListUtil.toList(resourceBO));
    }

    @Override
    public void assembleBaseInfo(Collection<? extends ResourceBO> resources) {
        Set<String> userIds = resources.stream()
            .map(resource -> CollUtil.newHashSet(resource.getCreatorId(), resource.getEditorId()))
            .flatMap(Collection::stream).collect(Collectors.toSet());
        List<UserAccountBO> userAccounts = userAccountService.listAccounts(userIds);
        Map<String, String> userMap = userAccounts.stream().collect(Collectors.toMap(
            UserAccountBO::getUserId,
            UserAccountBO::getUserName,
            (k1, k2) -> k1
        ));
        for (ResourceBO resource : resources) {
            resource.setCreatorName(userMap.getOrDefault(resource.getCreatorId(), ""));
            resource.setEditorName(userMap.getOrDefault(resource.getEditorId(), ""));
        }
    }

    @Override
    public ResourcesBO listSubResources(String supId, LogicQuery<Integer> status, int pathLevel, boolean withFile) {
        ResourceFolderStructBO folder = new ResourceFolderStructBO();
        folder.setPid(supId);
        this.listSubResources(CollUtil.toList(folder), status, pathLevel, withFile);
        ResourcesBO result = new ResourcesBO();
        result.setFolders(folder.getSubFolders());
        result.setFiles(folder.getSubFiles());
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void moveResources(List<String> folderList, List<String> fileList, String tarSupId, ConflictModeEnum conflictHandle) {
        fileService.moveFiles(fileList, tarSupId, conflictHandle);
        folderService.moveFolders(folderList, tarSupId, conflictHandle);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void copyResources(List<String> folderList, List<String> fileList, String tarSupId, ConflictModeEnum conflictHandle) {
        fileService.copyFiles(fileList, tarSupId, conflictHandle);
        folderService.copyFolders(folderList, tarSupId, conflictHandle);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void recycleResources(List<String> folderList, List<String> fileList) {
        fileService.recycleFiles(fileList);
        folderService.recycleFolders(folderList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void restoreResources(List<String> folderList, List<String> fileList, String tarSupId, ConflictModeEnum conflictHandle) {
        folderService.restoreFolders(folderList, tarSupId, conflictHandle);
        fileService.restoreFiles(fileList, tarSupId, conflictHandle);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteResources(List<String> folderList, List<String> fileList) {
        fileService.deleteFiles(fileList);
        folderService.deleteFolders(folderList);
    }

    private void listSubResources(List<ResourceFolderStructBO> supFolders, LogicQuery<Integer> status, int pathLevel, boolean withFile) {
        if (CollUtil.isEmpty(supFolders)) {
            return;
        }
        if (pathLevel == 0) {
            return;
        }
        List<String> supIds = supFolders.stream().filter(Objects::nonNull).map(ResourceFolderStructBO::getPid).collect(Collectors.toList());
        List<ResourceFolderStructBO> subFolders = folderService.listSubFolders(supIds, status);
        List<ResourceFileBO> subFiles = new ArrayList<>();
        if (withFile) {
            subFiles = fileService.listSubFiles(supIds, status, LogicQuery.none());
        }

        if ((--pathLevel) != 0) {
            this.listSubResources(subFolders, status, pathLevel, withFile);
        }

        Map<String, List<ResourceFolderStructBO>> subFolderMap = subFolders.stream().collect(Collectors.groupingBy(ResourceFolderStructBO::getSupId));
        Map<String, List<ResourceFileBO>> subFileMap = withFile ? subFiles.stream().collect(Collectors.groupingBy(ResourceFileBO::getSupId)) : new HashMap<>();

        for (ResourceFolderStructBO supFolder : supFolders) {
            supFolder.setSubFolders(subFolderMap.getOrDefault(supFolder.getPid(), new ArrayList<>()));
            if (withFile) {
                supFolder.setSubFiles(subFileMap.getOrDefault(supFolder.getPid(), new ArrayList<>()));
            }
        }
    }

    private void listSubResources(ResourceFolderStructBO supFolder, LogicQuery<Integer> status, int pathLevel, boolean withFile) {
        if (Objects.isNull(supFolder) || StrUtil.isBlank(supFolder.getPid()))
            return;
        if (pathLevel == 0)
            return;
        List<ResourceFolderStructBO> subFolders = folderService.listSubFolders(supFolder.getPid(), status);
        supFolder.setSubFolders(subFolders);
        if (withFile) {
            List<ResourceFileBO> subFiles = fileService.listSubFiles(supFolder.getPid(), status, LogicQuery.none());
            supFolder.setSubFiles(subFiles);
        }
        if ((--pathLevel) != 0) {
            for (ResourceFolderStructBO subFolder : subFolders) {
                this.listSubResources(subFolder, status, pathLevel, withFile);
            }
        }
    }
}
